<?php


/**
 * 使用ftp函数实现ftp相关操作.
 *
 * 使用方法和普通类库一样:
 * <code>
 *
 * $ftp = new WindFtp(array('server' => '192.168.1.10', 'port' => '21', ‘user' => 'test', 'pwd' => '123456'));
 * print_r($ftp->fileList());
 * </code>
 *
 * @author xiaoxia.xu <xiaoxia.xuxx@aliyun-inc.com>
 * @copyright ©2003-2103 phpwind.com
 * @license http://www.windframework.com
 *
 * @version $Id: WindFtp.php 3904 2013-01-08 07:01:26Z yishuo $
 */
class WindFtp extends AbstractWindFtp
{
    /**
     * 被动模式是否开启默认为true开启.
     *
     * @var bool
     */
    private $isPasv = true;

    /**
     * 构造函数.
     *
     * 通过传入config构造链接对象
     *
     * @param array $config ftp配置文件
     */
    public function __construct($config = array())
    {
        $this->connection($config);
    }

    /**
     * 链接ftp.
     *
     * @param array $config ftp的配置信息：
     *                      <ul>
     *                      <li>server: ftp主机地址</li>
     *                      <li>port: ftp链接端口号，默认为21</li>
     *                      <li>user: ftp链接用户名</li>
     *                      <li>pwd: ftp链接用户密码</li>
     *                      <li>dir: ftp链接后切换的目录,默认为空</li>
     *                      <li>timeout: ftp链接超时时间,默认为10秒</li>
     *                      <li>ispasv: ftp是否采用被动模式，默认为1，如果配置为0则表示不开启被动模式，其他值都将设置为开启被动模式</li>
     *                      </ul>
     *
     * @return bool
     */
    private function connection($config = array())
    {
        $this->initConfig($config);
        if (false === ($this->conn = ftp_connect($this->server, $this->port, $this->timeout))) {
            throw new WindFtpException("[ftp.WindFtp.connection] $this->server:$this->port",
                WindFtpException::CONNECT_FAILED);
        }
        if (false == ftp_login($this->conn, $this->user, $this->pwd)) {
            throw new WindFtpException('[ftp.WindFtp.connection] '.$this->user, WindFtpException::LOGIN_FAILED);
        }
        if ($this->isPasv) {
            ftp_pasv($this->conn, true);
        }
        $this->initRootPath();

        return true;
    }

    /**
     * 获得ftp链接.
     *
     * @return resource
     */
    private function getFtp()
    {
        if (is_resource($this->conn)) {
            return $this->conn;
        }
        $this->connection();

        return $this->conn;
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::rename()
     */
    public function rename($oldName, $newName)
    {
        return ftp_rename($this->getFtp(), $oldName, $newName);
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::delete()
     */
    public function delete($filename)
    {
        return ftp_delete($this->getFtp(), $filename);
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::upload()
     */
    public function upload($sourceFile, $desFile, $mode = 'I')
    {
        $mode = $this->getMode($sourceFile, $mode);
        if (!in_array(($savedir = dirname($desFile)), array('.', '/'))) {
            $this->mkdirs($savedir);
        }
        $desFile = $this->rootPath.WindSecurity::escapePath($desFile);
        $result = ftp_put($this->getFtp(), $desFile, $sourceFile, $mode);
        if (false === $result) {
            throw new WindFtpException('[ftp.WindFtp.upload] PUT', WindFtpException::COMMAND_FAILED);
        }
        $this->chmod($desFile, 0644);

        return $this->size($desFile);
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::download()
     */
    public function download($filename, $localname = '', $mode = 'I')
    {
        $mode = $this->getMode($filename, $mode);

        return ftp_get($this->getFtp(), $localname, $filename, $mode);
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::fileList()
     */
    public function fileList($dir = '')
    {
        return ftp_nlist($this->getFtp(), $dir);
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::close()
     */
    public function close()
    {
        is_resource($this->conn) && ftp_close($this->conn);
        $this->conn = null;

        return true;
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::initConfig()
     */
    public function initConfig($config)
    {
        if (!$config || !is_array($config)) {
            return false;
        }
        parent::initConfig($config);
        $this->isPasv = (isset($config['ispasv']) && $config['ispasv'] == 0) ? false : true;
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::mkdir()
     */
    public function mkdir($dir, $permissions = 0777)
    {
        $result = ftp_mkdir($this->getFtp(), $dir);
        if (!$result) {
            return false;
        }

        return $this->chmod($result, $permissions) === false ? false : true;
    }

    /**
     * 给文件赋指定权限.
     *
     * @param string $file        待处理的文件
     * @param int    $permissions 文件的需要的权限
     *
     * @return bool 设置成功返回true,设置失败返回false
     */
    private function chmod($file, $permissions = 0777)
    {
        return ftp_chmod($this->getFtp(), $permissions, $file);
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::pwd()
     */
    protected function pwd()
    {
        return ftp_pwd($this->getFtp()).'/';
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::changeDir()
     */
    public function changeDir($dir)
    {
        if (false === ftp_chdir($this->getFtp(), $dir)) {
            throw new WindFtpException('[ftp.WindFtp.changeDir] '.$dir, WindFtpException::COMMAND_FAILED_CWD);
        }

        return true;
    }

    /* (non-PHPdoc)
     * @see AbstractWindFtp::size()
     */
    public function size($file)
    {
        return ftp_size($this->getFtp(), $file);
    }

    /**
     * 根据文件获得文件访问的模式.
     *
     * @param string $filename 文件名
     * @param string $mode     模式，二进制还是ASCII上传，I为二进制模式，A为ASCII模式，默认为A模式，如果是auto将会根据文件后缀来设置模式
     *
     * @return string 返回模式方式FTP_ASCII或是FTP_BINARY
     */
    private function getMode($filename, $mode)
    {
        if (strcasecmp($mode, 'auto') == 0) {
            $ext = WindFile::getSuffix($filename);
            $mode = (in_array(strtolower($ext),
                array(
                    'txt',
                    'text',
                    'php',
                    'phps',
                    'php4',
                    'js',
                    'css',
                    'htm',
                    'html',
                    'phtml',
                    'shtml',
                    'log',
                    'xml', ))) ? 'A' : 'I';
        }

        return ($mode == 'A') ? FTP_ASCII : FTP_BINARY;
    }
}
